<template>
  <div ref="tabs">
    <!-- model-value:表示的是要激活name为activeTabName的tab-pane-->
    <el-tabs :model-value="activeTabName" @tab-click="tabClick" @contextmenu.prevent.native="openContextMenu"
             @tab-remove="closeTab">
      <el-tab-pane :name="item.path" v-for="item in openedTabs" :key="item.path" :label="item.text"
                   :closable="true">
      </el-tab-pane>
    </el-tabs>

    <ul v-show="contextMenuVisible" :style="{ left: left + 'px', top: top + 'px' }" class="context-menu">
      <li @click="refreshTab">
        <el-icon>
          <Refresh/>
        </el-icon>
        刷新页面
      </li>
      <li @click="closeCurrent()">
        <el-icon>
          <Close/>
        </el-icon>
        关闭当前
      </li>
      <li v-show="otherVisible" @click="closeOthers">
        <el-icon>
          <CircleClose/>
        </el-icon>
        关闭其他
      </li>
      <li v-show="leftVisible" @click="closeLeft">
        <el-icon>
          <DArrowLeft/>
        </el-icon>
        关闭左侧
      </li>
      <li v-show="rightVisible" @click="closeRight">
        <el-icon>
          <DArrowRight/>
        </el-icon>
        关闭右侧
      </li>
      <li @click="closeAll()">
        <el-icon>
          <CircleClose/>
        </el-icon>
        全部关闭
      </li>
    </ul>
  </div>
</template>

<script setup lang='ts'>
import {reactive, ref, toRef, watch} from "vue";
import {useRoute, useRouter} from "vue-router";
import {useTabStore} from '@/store/useTabStore';
import {type TabsPaneContext} from "element-plus";
import {type TabDefine} from "@/types/common.d.ts";

let tabs = ref()
let activeTabName = ref('')
// 上下文菜单是否显示
let contextMenuVisible = ref(false)
let left = ref(0)
let top = ref(0)
let otherVisible = ref(false)
let leftVisible = ref(false)
let rightVisible = ref(false)

// 右键上下文菜单出现时,光标所在的tab
let locatedTab = reactive<TabDefine>({path: '', text: '', fullPath: ""})
let router = useRouter()
let currentRoute = useRoute()
let tabStore = useTabStore()
let openedTabs = toRef(tabStore.openedTabInfo, "tabs")

// 监听contextMenuVisible.value的变化
watch(contextMenuVisible, () => {
  if (contextMenuVisible.value) {
    document.body.addEventListener("click", closeContextMenu)
  } else {
    document.body.removeEventListener("click", closeContextMenu)
  }
})

// 监听路由变化
watch(currentRoute, (newValue, oldValue) => {
  // 设置激活的tab
  activeTabName.value = newValue.path
  if ("/" === newValue.path) return
  // 把路由对象转换成tab存储到pinia中
  let tab: TabDefine = {
    path: newValue.path,
    text: newValue.meta.authorityName as string,
    fullPath: newValue.fullPath,
    query: newValue.query
  }
  tabStore.addTabIfNotExist(tab)
}, {
  immediate: true
})

function tabClick(tab: TabsPaneContext) {
  let clickedTab = openedTabs.value.find(obj => obj.path === tab.paneName) as TabDefine
  router.replace(clickedTab.fullPath)
}

// 打开上下文菜单
function openContextMenu(e: PointerEvent) {
  // 只有真正点击的是tab-pane的时候才打开上下文菜单
  // 每个tab-pane的id="tab-"+tab-pane的name,因为
  // 我配置的name="item.path",故id格式为"tab-/user/manage"之类
  let tabIndex = (e.target as HTMLElement).id.indexOf("tab-")
  if (tabIndex === -1) {
    return
  }
  const contextMenuMinWidth = 105
  // 获取当前组件最外层元素的宽度
  const offsetWidth = tabs.value.offsetWidth
  // 距离左边的最大距离,超过这个距离上下文菜单将被遮盖
  const maxLeft = offsetWidth - contextMenuMinWidth
  const clientX = e.clientX - 15
  if (clientX > maxLeft) {
    left.value = maxLeft
  } else {
    left.value = clientX
  }
  top.value = e.clientY + 20
  // 设置右键菜单可见
  contextMenuVisible.value = true
  let start = tabIndex + "tab-".length;
  // 截取到路由的path,如/user/manage
  let locatedTabPath = (e.target as HTMLElement).id.substring(start)
  // 保存光标所在的tab
  locatedTab = findTabByPath(locatedTabPath) as TabDefine
  // 动态实时的决定菜单项是否显示
  decideLeftVisible()
  decideRightVisible()
  decideOtherVisible()
}

function closeContextMenu() {
  contextMenuVisible.value = false
}

function decideLeftVisible() {
  leftVisible.value = locatedTab.path !== openedTabs.value[0].path
}

function decideRightVisible() {
  rightVisible.value = locatedTab.path !== openedTabs.value[openedTabs.value.length - 1].path
}

function decideOtherVisible() {
  otherVisible.value = openedTabs.value.length > 1
}

function findTabByPath(path: string): TabDefine | undefined {
  return openedTabs.value.find(valObj => valObj.path === path)
}

function closeTab(tabName: string) {
  // 关闭的是激活项
  if (tabName === activeTabName.value) {
    if (openedTabs.value.length > 1) {
      // 获取光标所在tab的下标
      let tabToCloseIndex = openedTabs.value.findIndex(valObj => valObj.path === tabName);
      // 声明类型
      let tabToActive: TabDefine
      // 已经是第一个
      if (tabToCloseIndex === 0) {
        // 激活右侧一个
        tabToActive = openedTabs.value[tabToCloseIndex + 1]
      } else {
        // 激活左侧一个
        tabToActive = openedTabs.value[tabToCloseIndex - 1]
      }
      // 改变地址栏的地址
      router.replace(tabToActive.fullPath)
    } else {
      //只有1个tab时
      router.replace("/")
    }
  }
  // 关闭tab
  tabStore.closeTab(tabName)
}

function refreshTab() {
  router.replace({
    path: '/redirect' + locatedTab.path,
    query: locatedTab.query
  })
}

// 关闭当前tab
function closeCurrent() {
  closeTab(locatedTab.path)
}

// 关闭其他
function closeOthers() {
  tabStore.closeOthers(locatedTab)
  // 当前光标不在激活项上,说明激活项被关闭
  if (locatedTab.path !== activeTabName.value) {
    router.replace(locatedTab.fullPath)
  }
}

function closeLeft() {
  tabStore.closeLeft(locatedTab)
  // 当前光标不在激活项上
  if (locatedTab.path !== activeTabName.value) {
    let activeTab = findTabByPath(activeTabName.value)
    if (!activeTab) { // 说明激活项被关闭
      router.replace(locatedTab.fullPath)
    }
  }
}

function closeRight() {
  tabStore.closeRight(locatedTab)
  // 当前光标不在激活项上
  if (locatedTab.path !== activeTabName.value) {
    let activeTab = findTabByPath(activeTabName.value)
    if (!activeTab) { // 说明激活项被关闭
      router.replace(locatedTab.fullPath)
    }
  }
}

function closeAll() {
  tabStore.closeAll()
  router.replace("/")
}
</script>

<style scoped>
.context-menu {
  margin: 0;
  background: #fff;
  z-index: 3000;
  position: absolute;
  list-style-type: none;
  padding: 5px 0;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 400;
  color: #333;
  box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
}

.context-menu li {
  margin: 0;
  padding: 7px 16px;
  cursor: pointer;
}

.context-menu li :hover {
  background: #eee;
}
</style>